package com.vftour.qichongbook.framework.mp.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vftour.qichongbook.framework.constant.Constant;
import com.vftour.qichongbook.framework.mp.base.Entity;
import com.vftour.qichongbook.framework.mp.service.IBaseService;
import com.vftour.qichongbook.framework.secure.auth.CurrentUser;
import com.vftour.qichongbook.framework.secure.util.AuthUtil;
import com.vftour.qichongbook.framework.util.ClassUtil;
import com.vftour.qichongbook.framework.util.Func;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotEmpty;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * BaseServiceImpl
 *
 * @author 东东 | d@tke.store
 * @date 2022/7/1 7:51
 * @since 1.0
 */

@Validated
public class BaseServiceImpl<M extends BaseMapper<T>, T extends Entity> extends ServiceImpl<M, T> implements IBaseService<T> {

    @Override
    public boolean save(T entity) {
        this.resolveEntity(entity);
        return super.save(entity);
    }

    @Override
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        entityList.forEach(this::resolveEntity);
        return super.saveBatch(entityList, batchSize);
    }

    @Override
    public boolean updateById(T entity) {
        this.resolveEntity(entity);
        return super.updateById(entity);
    }

    @Override
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        entityList.forEach(this::resolveEntity);
        return super.updateBatchById(entityList, batchSize);
    }

    @Override
    public boolean saveOrUpdate(T entity) {
        if (entity.getId() == null) {
            return this.save(entity);
        } else {
            return this.updateById(entity);
        }
    }

    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
        entityList.forEach(this::resolveEntity);
        return super.saveOrUpdateBatch(entityList, batchSize);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteLogic(@NotEmpty List<Long> ids) {
        CurrentUser user = AuthUtil.getUser();
        List<T> list = new ArrayList<>();
        ids.forEach(id -> {
            T entity = BeanUtils.instantiateClass(currentModelClass());
            if (user != null) {
                entity.setUpdateUser(user.getUserId());
            }
            entity.setUpdateTime(LocalDateTime.now());
            entity.setId(id);
            list.add(entity);
        });
        return super.updateBatchById(list) && super.removeByIds(ids);
    }

    @SneakyThrows
    private void resolveEntity(T entity) {
        CurrentUser user = AuthUtil.getUser();
        LocalDateTime now = LocalDateTime.now();
        if (entity.getId() == null) {
            // 处理新增逻辑
            if (user != null) {
                entity.setCreateUser(user.getUserId());
                entity.setCreateDept(Func.firstLong(user.getDeptId()));
                entity.setUpdateUser(user.getUserId());
            }
            if (entity.getStatus() == null) {
                entity.setStatus(Constant.DB_STATUS_NORMAL);
            }
            entity.setCreateTime(now);
        } else if (user != null) {
            // 处理修改逻辑
            entity.setUpdateUser(user.getUserId());
        }
        // 处理通用逻辑
        entity.setUpdateTime(now);
        entity.setIsDeleted(Constant.DB_NOT_DELETED);
        // 处理多租户逻辑，若字段值为空，则不进行操作
        Field field = ReflectUtil.getField(entity.getClass(), Constant.DB_TENANT_KEY);
        if (ObjectUtil.isNotEmpty(field)) {
            Method getTenantId = ClassUtil.getMethod(entity.getClass(), Constant.DB_TENANT_KEY_GET_METHOD);
            String tenantId = String.valueOf(getTenantId.invoke(entity));
            if (ObjectUtil.isEmpty(tenantId)) {
                Method setTenantId = ClassUtil.getMethod(entity.getClass(), Constant.DB_TENANT_KEY_SET_METHOD, String.class);
                setTenantId.invoke(entity, (Object) null);
            }
        }
    }
}
